#include "evnet/http/server.hpp"
#include "evnet/http/reactor.hpp"
#include "evnet/http/option.hpp"
#include "evnet/http/info.hpp"
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#ifdef WIN32
#include <io.h>
#endif


class application
{
    evnet::queue queue_;
    evnet::http::server server_;
    evnet::http::reactor reactor_;
    evnet::http::operation webop1_;
    evnet::http::operation webop2_;
    evnet::http::operation webop3_;
    std::size_t counter_{0};

    std::string cert_ = "ca.crt";
    std::string key_ = "ca.key";
public:

    application()
        : queue_()
        , server_(queue_)
        , reactor_(queue_)
        , webop1_(std::move(std::bind(&application::done1, this, std::placeholders::_1)))
        , webop2_("https://localhost:8433/OK", std::bind(&application::done2, this, std::placeholders::_1))
        , webop3_("https://localhost:8433/OK", std::bind(&application::done3, this, std::placeholders::_1))
    {
        webop1_.set(evnet::http::username("text"));
        webop1_.set(evnet::http::password("text"));
        webop2_.set(evnet::http::username("text"));
        webop2_.set(evnet::http::password("text"));
        webop3_.set(evnet::http::username("text"));
        webop3_.set(evnet::http::password("text"));
        webop1_.set("https://localhost:8433/OK");

        server_.get("/OK", std::bind(&application::location2, this, std::placeholders::_1));

        evhtp_ssl_cfg_t scfg = {};
        scfg.pemfile = const_cast<char*>(cert_.c_str());
        scfg.privfile = const_cast<char*>(key_.c_str());
        scfg.ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
        scfg.ciphers = const_cast<char*>("RC4+RSA:HIGH:+MEDIUM:+LOW"),
        scfg.ssl_ctx_timeout = 60 * 60 * 48,
        scfg.verify_peer = SSL_VERIFY_NONE,
        scfg.verify_depth = 42,
        scfg.x509_verify_cb = 0,
        scfg.x509_chk_issued_cb = 0,
        scfg.scache_type = evhtp_ssl_scache_type_internal,
        scfg.scache_size = 1024,
        scfg.scache_timeout = 1024,
        scfg.scache_init = 0,
        scfg.scache_add = 0,
        scfg.scache_get = 0,
        scfg.scache_del = 0;

        server_.init_ssl(scfg);

        server_.start("0.0.0.0", 8433);
    }

    void start1()
    {
        //std::cout << std::endl << " !!! START !!! " << std::endl;
        webop1_.start(reactor_);
    }

    void done1(CURLcode error)
    {
        auto& b = webop1_.output();
        auto size = b.size();
        if (error != CURLE_OK)
            std::cout << "client : " << evnet::http::description(error) << " : " << std::string((const char*)b.pullup(size), size) << std::endl;

        start1();
    }

    void start2()
    {
        //std::cout << std::endl << " !!! START !!! " << std::endl;
        webop2_.start(reactor_);
    }

    void done2(CURLcode error)
    {
        auto& b = webop2_.output();
        auto size = b.size();
        if (error != CURLE_OK)
            std::cout << "client : " << evnet::http::description(error) << " : " << std::string((const char*)b.pullup(size), size) << std::endl;
        //else
            //std::cout << "client : " << evnet::http::response_code(webop2_) << std::endl;

        start2();
    }

    void start3()
    {
        //std::cout << std::endl << " !!! START !!! " << std::endl;
        webop3_.start(reactor_);

        //webop_.get("http://lalala.ru");
        //webop_.post("http://lalala.ru", std::move(buffer));
    }

    void done3(CURLcode error)
    {
        auto& b = webop3_.output();
        auto size = b.size();
        if (error != CURLE_OK)
            std::cout << "client : " << evnet::http::description(error) << " : " << std::string((const char*)b.pullup(size), size) << std::endl;

        start3();
    }

    int run()
    {
        start1();
        start2();
        start3();
        //queue_.loopexit(std::chrono::seconds(10));
        queue_.dispatch();
        server_.stop();
        return 0;
    }

    void location2(evnet::http::request request)
    {

        auto auth = request.header("Authorization");
        if (auth.empty())
        {
            request.set("WWW-Authenticate", "Basic realm=\"My Server\"");
            request.send(evnet::http::reply::unauthorized());
            return;
        }

        std::string text{std::to_string(++counter_)};
        if ((counter_ % 100) == 0)
            std::cout << "server : " << text << std::endl;

        evnet::buffer out;
        out.append(text);
        request.send(evnet::http::reply::ok(std::move(out)));
    }
};

int main(int, char**)
{
    evnet::buffer a;

    {
        evnet::buffer b;
        a.append("123");
        b.append("456");

        std::cout << "b: " << std::string((const char*)b.pullup(b.size()), b.size()) << std::endl;
        a.add_buffer(std::move(b));
    }

    std::cout << "a: " << std::string((const char*)a.pullup(a.size()), a.size()) << std::endl;

#ifdef _WIN32
    {
        WSADATA WSAData;
        WSAStartup(0x101, &WSAData);
    }
#endif
    try
    {
        application app;
        return app.run();
    }
    catch (const std::exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    catch (...)
    {
        std::cerr << "error..." << std::endl;
    }
    return 0;
}
